package com.yz.sharkim.handler;

import com.yz.sharkim.annotation.*;
import com.yz.sharkim.entity.message.BaseMessage;
import com.yz.sharkim.util.BeanUtil;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpMethod;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.context.ConfigurableApplicationContext;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Slf4j
public class DispatchRouterHandler {

    private static final String URL_SEPARATOR = "/";

    public static final String URI_PARAMETER_SEPARATOR = "\\?";

    private static final Map<ApiKey, ApiValue> REST_MAP = new HashMap<>();

    private static final Map<Integer, ApiValue> WEB_SOCKET_MAP = new HashMap<>();

    /**
     * 扫描所有beans包含NettyController注解的类
     * @param context
     */
    public static void initRouter(ConfigurableApplicationContext context) {
        val beans = context.getBeanDefinitionNames();
        for (String bean : beans) {
            Class<?> clz = context.getType(bean);
            HttpController httpController = (HttpController) Objects.requireNonNull(clz).getAnnotation(HttpController.class);
            if (httpController != null) {
                for (Method method : clz.getMethods()) {
                    val get = method.getAnnotation(GetMapping.class);
                    val post = method.getAnnotation(PostMapping.class);
                    if (get != null) {
                        val uri = URL_SEPARATOR + httpController.value() + URL_SEPARATOR + get.value();
                        REST_MAP.put(new ApiKey(HttpMethod.GET, uri),
                                new ApiValue(clz, context.getBean(bean), method));
                    }
                    if (post != null) {
                        val uri = URL_SEPARATOR + httpController.value() + URL_SEPARATOR + post.value();
                        REST_MAP.put(new ApiKey(HttpMethod.POST, uri),
                                new ApiValue(clz, context.getBean(bean), method));
                    }
                }
            }
            val webSocketController = (WebSocketController) clz.getAnnotation(WebSocketController.class);
            if (webSocketController != null) {
                for (Method method : clz.getMethods()) {
                    val id = method.getAnnotation(IdMapping.class);
                    if (id != null) {
                        WEB_SOCKET_MAP.put(id.value(), new ApiValue(clz, context.getBean(bean), method));
                    }
                }
            }
        }
    }

    public static ApiValue get(FullHttpRequest request) {
        val uri = request.uri().split(URI_PARAMETER_SEPARATOR);
        return REST_MAP.get(new ApiKey(request.method(), uri[0]));
    }

    public static ApiValue get(BaseMessage entity) {
        return WEB_SOCKET_MAP.get(entity.getId());
    }

    private static String recycleReplace(String str) {
        str = str.replaceAll(URL_SEPARATOR + URL_SEPARATOR, URL_SEPARATOR);
        if (str.contains(URL_SEPARATOR + URL_SEPARATOR)) {
            return recycleReplace(str);
        } else {
            return str;
        }
    }

    @Data
    static class ApiKey {

        public final static String URL_SEPARATOR = "/";

        private HttpMethod httpMethod;

        private String uri;

        public ApiKey(HttpMethod httpMethod, String uri) {
            this.httpMethod = httpMethod;
            this.uri = recycleReplace(uri);
            log.info("Scan method [{}] {}", this.httpMethod.toString(), this.uri);
        }
    }

    @Data
    static class ApiValue {

        private Class clz;

        private Method method;

        private Object object;


        public ApiValue(Class clz, Object object, Method method) {
            this.clz = clz;
            this.object = object;
            this.method = method;
        }
    }


}
